package com.csx.base.concurrency.container;

import com.csx.base.concurrency.util.TimeUtil;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;

/**
 * <p> what does this class do ? </p>
 *   该案例是 BlockingQueue 旗下类的使用案例
 *   描述:
 *     该案例是一个典型的生产者-消费者模型.
 *       当 queue 中有空位时,生产者生产产品放入到queue中,唤醒阻塞的消费者;当 queue 满了后,生产者被阻塞。
 *       当 queue 中有产品时,消费者从中取出产品消费,唤醒阻塞的生产者; 当 queue 空了后,消费者被阻塞
 *   BlockingQueue的使用场景:
 *    1.  适用在需要线程同步协调的场景。例如 生产者-消费者 模型(线程池阻塞队列)
 *
 * <p> How does it work ?</p>
 * <p> Why does it do this ?</p>
 *
 * @author cuisongxu
 * @date 2024/12/6 周五 8:47
 */
public class BlockQueueTest {

    // 存储产品的队列
    private final ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);

    // 控制生产者生产的变量
    private volatile boolean productStart = true;

    // 控制消费者消费的变量
    private volatile boolean consumeStart = true;


    public static void main(String[] args) {
        BlockQueueTest blockQueueTest = new BlockQueueTest();
        blockQueueTest.test();
    }

    // 入口方法
    public void test() {

        // 创建一个生产者 和 两个消费者
        Thread producer = new Thread(new Producer(), "producer1");
        Thread consumer1 = new Thread(new Consumer(), "consumer1");
        Thread consumer2 = new Thread(new Consumer(), "consumer2");

        // 启动所有生产者 和 消费者
        producer.start();
        consumer1.start();
        consumer2.start();

        // 等待
        try {
            producer.join();
            consumer1.join();
            consumer2.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

    }
    class Producer implements Runnable {

        @Override
        public void run() {

            try {
                while(productStart) {

                    Integer product = (int) (Math.random() * 100 + 100);
                    // 模拟生产耗时
                    TimeUnit.MILLISECONDS.sleep(300);
                    queue.put(product);

                    String nowTimeStr = TimeUtil.getNowTimeStr();
                    System.out.println(nowTimeStr + " " + Thread.currentThread().getName() + "生产了产品: " + product);
                }

            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    class Consumer implements Runnable {

        @Override
        public void run() {

            try {
                while(consumeStart) {
                    // 模拟消费耗时
                    TimeUnit.SECONDS.sleep(1);
                    Integer product = queue.take();

                    String nowTimeStr = TimeUtil.getNowTimeStr();
                    System.out.println(nowTimeStr + " " + Thread.currentThread().getName() + "消费了产品: " + product);
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
